/**
 * @file GC.hpp
 *
 *  @date 2009-06-20
 *  @author hauleth
 */

#ifndef GC_HPP_
#define GC_HPP_

#include <map>
#include <list>
#include <cstdlib>

namespace bust {
namespace {

std::list< int(*)() > collect_func_l;

/**
 * @class GCptr
 * @brief Class for managed pointers
 * If you using this class as pointers you can forget about memory leaks.
 */
template < typename T >
class GCptr {
	private:
		typedef std::map< T*, int > ref_map;
		T* ptr;
		static ref_map ref;
		static bool init;
		static void ate() {
			Collect();
		}
	public:
		/**
		 * Construct GCptr from given pointer.
		 * Example of usage:
		 * @code bust::GCptr< int > ptr = new int;
		 * @param pointer
		 */
		GCptr( T* pointer = NULL ) :
			ptr( pointer ) {
			ref[ptr]++;
			if( !init ) {
				atexit( ate );
				collect_func_l.push_back( Collect );
				init = true;
			}
		}
		virtual ~GCptr() {
			if( ! ( --ref[ptr] ) ) {
				ref.erase( ptr );
				delete ptr;
				ptr = NULL;
			}
		}

		GCptr( const GCptr< T >& cp ) {
			ptr = cp.ptr;
			ref[ptr]++;
		}
		GCptr& operator =( T* pointer ) {
			ref[ptr]--;
			ptr = pointer;
			ref[ptr]++;
			return *this;
		}
		GCptr& operator =( const GCptr< T >& cp ) {
			ref[ptr]--;
			ptr = cp.ptr;
			ref[ptr]++;
			return *this;
		}

		operator bool() const {
			return ptr != NULL;
		}
		bool operator !() const {
			return ptr == NULL;
		}

		T& operator*() {
			return *ptr;
		}
		const T& operator*() const {
			return *ptr;
		}
		T* operator ->() {
			return ptr;
		}

		T* pointer() {
			return ptr;
		}
		const T* pointer() const {
			return ptr;
		}

		static int Collect();
};

template < typename T >
int GCptr< T >::Collect() {
	int ret = 0;
	std::list< typename ref_map::iterator > rem;
	for( typename ref_map::iterator i = ref.begin(); i != ref.end(); i++ )
		if( !i->second ) {
			delete i->first;
			ret++;
			rem.push_back( i );
		}
	for( typename std::list< typename ref_map::iterator >::iterator i =
			rem.begin(); i != rem.end(); ++i )
		ref.erase( *i );
	return ret;
}

int Collect() {
	int ret = 0;
	for( std::list< int(*)() >::iterator i = collect_func_l.begin(); i
			!= collect_func_l.end(); ++i )
		ret += ( *i )();
	return ret;
}

template < typename T >
std::map< T*, int > GCptr< T >::ref;

template < typename T >
bool GCptr< T >::init = false;

}
}

#endif /* GC_HPP_ */
